<

Web 上の Raw 画像は正しい原点と色を使用しています

まとめ

Web 上での RAW 画像のレンダリング方法が修正されました 他のプラットフォームのものと一致するようになりました。 これにより、誤ったデータをフィードする必要があった従来のアプリが壊れます にui.ImageDescriptor.rawまたui.decodeImageFromPixels、 結果として得られる画像が上下逆さまになってしまう そして間違った色で (赤と青のチャンネルが交換されます。)

コンテクスト

Flutterが内部的に使用する「ピクセルストリーム」 は常に同じ形式として定義されています。 ピクセルごとに、4 つの 8 ビット チャネルが定義された順序でパックされます。 によってformat引数を一行にグループ化し、 左から右に、次に上から下の行に進みます。

ただし、Flutter for Web、より具体的には HTML レンダラは、 間違った方法で実装されていた BMP 形式の仕様についての誤った理解が原因です。 その結果、アプリまたはライブラリがui.ImageDescriptor.rawまたui.decodeImageFromPixels、 ピクセルを下から上に送り、赤と青のチャンネルを交換する必要がありました。 (たとえば、ui.PixelFormat.rgba8888フォーマット、 データの最初の 4 バイトは青、緑、 代わりに、最初のピクセルの赤とアルファ チャネルが使用されます。)

このバグは次の方法で修正されましたエンジン#29593、 ただし、アプリとライブラリはデータの生成方法を修正する必要があります。

変更内容の説明

pixelsの議論ui.ImageDescriptor.rawまたui.decodeImageFromPixelsで記述された正しいピクセル順序が使用されるようになりました。format、 左上隅から始まります。

これら 2 つの関数を直接呼び出してレンダリングされた画像 これらの関数を直接呼び出す従来のコードは、 画像が逆さまで色が間違っているのを見つけます。

移行ガイド

アプリが最新バージョンの Flutter を使用していてこの状況が発生した場合、 最も直接的な解決策は、画像を手動で反転し、代替手段を使用することです。 ピクセル形式。ただし、これが最も最適化されたソリューションである可能性は低く、 このようなピクセル データは通常、他のソースから構築されるため、 構築プロセス中に反転できるようになります。

移行前のコード:

import 'dart:typed_data';
import 'dart:ui' as ui;

// Parse `image` as a displayable image.
//
// Each byte in `image` is a pixel channel, in the order of blue, green, red,
// and alpha, starting from the bottom left corner and going row first.
Future<ui.Image> parseMyImage(Uint8List image, int width, int height) async {
  final ui.ImageDescriptor descriptor = ui.ImageDescriptor.raw(
    await ui.ImmutableBuffer.fromUint8List(image),
    width: width,
    height: height,
    pixelFormat: ui.PixelFormat.rgba8888,
  );
  return (await (await descriptor.instantiateCodec()).getNextFrame()).image;
}

移行後のコード:

import 'dart:typed_data';
import 'dart:ui' as ui;

Uint8List verticallyFlipImage(Uint8List sourceBytes, int width, int height) {
  final Uint32List source = Uint32List.sublistView(ByteData.sublistView(sourceBytes));
  final Uint32List result = Uint32List(source.length);
  int sourceOffset = 0;
  int resultOffset = 0;
  for (final int row = height - 1; row >= 0; row -= 1) {
    sourceOffset = width * row;
    for (final int col = 0; col < width; col += 1) {
      result[resultOffset] = source[sourceOffset];
      resultOffset += 1;
      sourceOffset += 1;
    }
  }
  return Uint8List.sublistView(ByteData.sublistView(sourceBytes))
}

Future<ui.Image> parseMyImage(Uint8List image, int width, int height) async {
  final Uint8List correctedImage = verticallyFlipImage(image, width, height);
  final ui.ImageDescriptor descriptor = ui.ImageDescriptor.raw(
    await ui.ImmutableBuffer.fromUint8List(correctedImage),
    width: width,
    height: height,
    pixelFormat: ui.PixelFormat.rgba8888,
  );
  return (await (await descriptor.instantiateCodec()).getNextFrame()).image;
}

さらに厄介な状況は、ライブラリを作成しているときです。 そして、このライブラリを最新の Flutter の両方で動作させたいとします。 そしてパッチ前のものも。 その場合、動作が変更されたかどうかを判断できます 最初に単一ピクセルをデコードさせることによって。

移行後のコード:

Uint8List verticallyFlipImage(Uint8List sourceBytes, int width, int height) {
  // Same as the example above.
}

late Future<bool> imageRawUsesCorrectBehavior = (() async {
  final ui.ImageDescriptor descriptor = ui.ImageDescriptor.raw(
    await ui.ImmutableBuffer.fromUint8List(Uint8List.fromList(<int>[0xED, 0, 0, 0xFF])),
    width: 1, height: 1, pixelFormat: ui.PixelFormat.rgba8888);
  final ui.Image image = (await (await descriptor.instantiateCodec()).getNextFrame()).image;
  final Uint8List resultPixels = Uint8List.sublistView(
    (await image.toByteData(format: ui.ImageByteFormat.rawStraightRgba))!);
  return resultPixels[0] == 0xED;
})();

Future<ui.Image> parseMyImage(Uint8List image, int width, int height) async {
  final Uint8List correctedImage = (await imageRawUsesCorrectBehavior) ?
    verticallyFlipImage(image, width, height) : image;
  final ui.ImageDescriptor descriptor = ui.ImageDescriptor.raw(
    await ui.ImmutableBuffer.fromUint8List(correctedImage), // Use the corrected image
    width: width,
    height: height,
    pixelFormat: ui.PixelFormat.bgra8888, // Use the alternate format
  );
  return (await (await descriptor.instantiateCodec()).getNextFrame()).image;
}

タイムライン

リリースされたバージョン: 2.9.0-0.0.pre
安定版リリース: 2.10

参考文献

API ドキュメント:

  • decodeImageFromPixels
  • ImageDescriptor.raw

関連する問題:

  • Web: マスターの回帰 - BMP エンコーダーの変更により PDF 表示が歪む
  • Web: ImageDescriptor.raw は画像を反転および反転します (部分的な理由を含む)

関連する PR:

  • Web: Reland: BMP エンコーダを修正
  • ImageDescriptor.raw のピクセル順序を明確にし、バージョン検出器を追加します